fix(context): inject project dream namespaces alongside recent raw memory (closes #2834)#2858
fix(context): inject project dream namespaces alongside recent raw memory (closes #2834)#2858rodboev wants to merge 9 commits into
Conversation
Greptile SummaryThis PR fixes distilled (
Confidence Score: 5/5Safe to merge; the change is read-path only and degrades gracefully when dream rows are absent or sparse. The core logic — wiring the dream namespace into allProjects, gating on countObservationsByProjects before enabling the multi-query path, and the raw-row fallback — is straightforward and well-covered by the new tests. queryObservations now correctly selects o.project so getPriorSessionMessages can filter dream rows, and the include-last-message-dot-path test verifies the end-to-end behaviour with a dream row present. The two observations raised are minor: a null-project row could trigger an unnecessary extra fallback DB call, and the telemetry search_strategy field does not distinguish the new multi-query path — neither affects correctness or user-facing behaviour. No files require special attention; ObservationCompiler.ts has one minor defensive-null gap in includeRawFallback worth a one-character fix. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["generateContextWithStats(input)"] --> B["getProjectContext(cwd)\nallProjects = [proj:dream, proj]"]
B --> C{input.projects\nsupplied?}
C -- yes --> D[use input.projects]
C -- no --> E[use context.allProjects]
D & E --> F["split into\ndreamProjects / rawProjects"]
F --> G{dreamProjects exist\nAND count > 0?}
G -- no --> H["queryProjects = rawProjects"]
G -- yes --> I["queryProjects = all projects\n(dream + raw)"]
H & I --> J{queryProjects.length > 1?}
J -- no --> K["queryObservations(single)"]
J -- yes --> L["queryObservationsMulti(multi)"]
L --> M["includeRawFallback()"]
M --> N{all selected rows\nare dream rows?}
N -- no --> O[return as-is]
N -- yes --> P["queryLatestRawObservation()\nreplace last dream row"]
P --> O
K & O --> Q["getPriorSessionMessages\nskips dream rows via isDreamProject()"]
Q --> R["buildContextOutput\nproject label = getPrimaryContextProject\n(last raw project)"]
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
A["generateContextWithStats(input)"] --> B["getProjectContext(cwd)\nallProjects = [proj:dream, proj]"]
B --> C{input.projects\nsupplied?}
C -- yes --> D[use input.projects]
C -- no --> E[use context.allProjects]
D & E --> F["split into\ndreamProjects / rawProjects"]
F --> G{dreamProjects exist\nAND count > 0?}
G -- no --> H["queryProjects = rawProjects"]
G -- yes --> I["queryProjects = all projects\n(dream + raw)"]
H & I --> J{queryProjects.length > 1?}
J -- no --> K["queryObservations(single)"]
J -- yes --> L["queryObservationsMulti(multi)"]
L --> M["includeRawFallback()"]
M --> N{all selected rows\nare dream rows?}
N -- no --> O[return as-is]
N -- yes --> P["queryLatestRawObservation()\nreplace last dream row"]
P --> O
K & O --> Q["getPriorSessionMessages\nskips dream rows via isDreamProject()"]
Q --> R["buildContextOutput\nproject label = getPrimaryContextProject\n(last raw project)"]
Prompt To Fix All With AIFix the following 2 code review issues. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 2
src/services/context/ObservationCompiler.ts:86
The guard uses `row.project && !isDreamProject(row.project)`, which is falsy when `row.project` is `null`. Any observation row with a null `project` column (possible for data written before the column was populated) is treated the same as a dream row: the `selected.some(...)` check fails to recognise it as a raw row, so `queryLatestRawObservation` is invoked unnecessarily. The result set is still correct, but you pay an extra DB round-trip on every context injection for projects that have legacy null-project rows.
```suggestion
if (selected.some(row => row.project != null && !isDreamProject(row.project))) return selected;
```
### Issue 2 of 2
src/services/context/ContextBuilder.ts:165
The `search_strategy` telemetry field only captures `'full'` vs `'timeline'`, but this PR introduced a third dimension: single-project vs multi-project query. A user who never uses `input.full` but whose dream namespace is populated will always hit `queryObservationsMulti`, and that change in query shape is invisible to the telemetry. Adding a `'multi'` variant (or a separate boolean field) would make it possible to track how often the new dream-augmented path is exercised in production.
```suggestion
search_strategy: full ? 'full' : 'timeline',
// TODO: expose useMultiQuery here once the stat shape is stable
```
Reviews (18): Last reviewed commit: "fix(telemetry): keep shared telemetry te..." | Re-trigger Greptile |
|
Added the worktree composite dream namespace to |
|
Review finding that blocks this one: |
96ae63d to
91a88e2
Compare
|
Fixed in the latest push (
|
9453e47 to
44336c5
Compare
13f6401 to
c1c8db0
Compare
Summary
Distilled memories stored under
<project>:dreamwere absent from normal project context injection because the default query path only used the cwd-derived raw project name. This PR adds the dream namespace to the default project list and keeps a bounded recent raw-memory fallback so distilled context becomes primary without hiding fresh undistilled observations.Why
generateContext()insrc/services/context/ContextBuilder.ts:105-128already supports multi-project queries, andqueryObservationsMulti()/querySummariesMulti()insrc/services/context/ObservationCompiler.ts:88-175already union rows across project lists. The gap was thatgetProjectContext()insrc/utils/project-name.ts:76-96never emitted the companion dream namespace for ordinary project sessions, so the multi-project path was not used for distilled memory at all.Scope
This PR is intentionally read-path only. It does not change how dream rows are written, does not migrate stored project names, and does not inject any unscoped global
dreamnamespace.Risk
The main risk is over-weighting old distilled rows and crowding out fresh raw context. The fix keeps that bounded by preserving a recent raw fallback and by leaving the rendered project label unchanged even though the underlying query list expands.
Verification / Test plan
bun test tests/context/observation-compiler.test.ts- 9 passed; includes the raw-row fallback when dream rows would otherwise saturate the selected result set.npm run build- passed locally and regenerated bundled context/worker artifacts.npm run lint:hook-io- passed locally.npm run lint:spawn-env- passed locally.bun test tests/utils/project-name.test.ts- not rerun as a full file; a previous full-file run had pre-existing Windows~expectation failures.npm run strip-comments:check- failed against the existing repo-wide comment-stripping baseline in check mode (Changed: 327), unrelated to this dream-namespace branch.Closes #2834